package com.roin.learn.activiti.service.impl;

import com.roin.learn.activiti.entity.Page;
import com.roin.learn.activiti.service.ProcessService;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.*;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.zip.ZipInputStream;

@Service
public class ProcessServiceImpl implements ProcessService {

    @Autowired
    ProcessEngine processEngine;
    @Autowired
    ProcessEngineConfiguration processEngineConfiguration;
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    ManagementService managementService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    HistoryService historyService;
    @Autowired
    TaskService taskService;
    @Autowired
    FormService formService;
    @Resource
    IdentityService identityService;

    @Value("${activiti.save.path}")
    private String activitiPath;

    /**
     * 查询流程实例的历史活动
     * @param processInstanceId 流程实例ID can not be null
     * @param assignee 任务执行人 can be null
     * @return
     */
    public List<HistoricActivityInstance> getHistoricActivityInstance(String processInstanceId,String assignee){
        HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).activityType("userTask");
        if (StringUtils.isNotBlank(assignee))
            query.taskAssignee(assignee);
        return query.list();
    }

    /**
     * 查询模型
     * @param modelId
     * @return
     */
    @Override
    public List<Model> queryModel(String modelId) {
        ModelQuery modelQuery=repositoryService.createModelQuery();
        if (StringUtils.isNotEmpty(modelId))
            modelQuery.deploymentId(modelId);
        return modelQuery.list();
    }

    @Override
    public void deleteModel(String modelId) {
        if (StringUtils.isNotEmpty(modelId)) {
            repositoryService.deleteModel(modelId);
        }
    }

    /**
     * 发布流程
     *
     * @param bpmnPath 流程路径 非空
     * @param name     流程图名称
     * @version 0.1.0 2016/12/04
     */
    public Deployment deployClasspathResource(String bpmnPath, String name) {
        /** 1:创建一个部署对象;2:添加部署的名称;3:从classpath的资源中加载，一次只能加载一个文件;4:完成部署 */
        Deployment deployment = repositoryService.createDeployment().name(name).addClasspathResource(bpmnPath).deploy();
        return deployment;
    }


    public Deployment deployInputStream(InputStream bpmnPath, InputStream pngPath, String name) {
        Deployment deployment = repositoryService.createDeployment().name(name).addInputStream(name + ".bpmn", bpmnPath).addInputStream(name + ".png", pngPath).deploy();
        return deployment;
    }

    public Deployment deployZipInputStream(ZipInputStream zipInputStream,String name){
        Deployment deployment = repositoryService.createDeployment().name(name).addZipInputStream(zipInputStream).deploy();
        return deployment;
    }


    public Deployment deployString(String bpmnXml, String name) {
        Deployment deployment = repositoryService.createDeployment().name(name).addString(name + ".bpmn", bpmnXml).deploy();
        return deployment;
    }

    /**
     * 删除流程定义
     *
     * @param id   流程id
     * @param flag ture删除启动的流程定义；false未启动的流程定义 默认false
     * @version 0.1.0 2016/12/04
     */

    public void deleteProcessDefinition(String id, boolean flag) {
        if (flag) {
            // 删除流程定义 包括启动过的流程
            repositoryService.deleteDeployment(id, flag);
        } else {
            // 只能删除没有启动的流程定义，如果流程启动则抛出异常
            repositoryService.deleteDeployment(id);// 删除流程定义 id为流程id
        }
    }

    /**
     * 启动流程实例
     *
     * @param processDefinitionKey 流程key
     * @version 0.1.0 2016/12/04
     */

    public ProcessInstance startProessInstance(String processDefinitionKey, String businessKey, Map<String, Object> jsnVariables) throws Exception {
        ProcessInstance processInstance = null;
        if (StringUtils.isNotBlank(businessKey)) {
            if (jsnVariables != null && !jsnVariables.isEmpty()) {
                processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, jsnVariables);
            } else {
                processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey);
            }
        } else {
            if (jsnVariables != null && !jsnVariables.isEmpty()) {
                processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, jsnVariables);
            } else {
                processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey);
            }
        }
        return processInstance;
    }

    /**
     * 查看所有流程实例的任务
     * @return
     * @throws Exception
     */
    public List<Task> queryTaskList(String processDefinitionKey) throws Exception {
        TaskQuery query = taskService.createTaskQuery();
        if(StringUtils.isNotBlank(processDefinitionKey))
            query.processDefinitionKey(processDefinitionKey);
        return query.list();
    }

    /**
     * 查看个人任务
     *
     * @param assignee 审核人
     * @version 0.1.0 2016/12/04
     */
    public Page<Task> queryAssigneeTask(String assignee, int pageNum, int pageSize) throws Exception {
        /** 这里使用TaskServ<ice完成任务的查询 */
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(assignee).listPage((pageNum-1)*pageSize,pageSize);
        long count  = taskService.createTaskQuery().taskAssignee(assignee).count();
        Page page =new Page(tasks);
        page.setTotal(count);
        return page;
    }
    /**
     * 查看个人任务数量
     *
     * @param assignee 审核人
     * @version 0.1.0 2016/12/04
     */
    public int queryAssigneeTaskCount(String assignee) throws Exception {
        /** 这里使用TaskService完成任务的查询 */
        return Long.valueOf(taskService.createTaskQuery().taskAssignee(assignee).count()).intValue();
    }

    /**
     * 查看流程实例的当前任务
     * @param processInstanceId
     * @param assignee 任务执行人 can be null
     * @return
     * @throws Exception
     */
    public Task queryActiviTask(String processInstanceId,String assignee) throws Exception {
        /** 这里使用TaskService完成任务的查询 */
        TaskQuery query = taskService.createTaskQuery().processInstanceId(processInstanceId).active();
        if (StringUtils.isNotBlank(assignee))
            query.taskAssignee(assignee);
        Task task = query.singleResult();
        return task;
    }


    public List<Task> queryTaskProcessInstanceId(String processInstanceId, Map<String, Object> jsnVariables) throws Exception {
        TaskQuery query = null;
        if (jsnVariables != null && StringUtils.isNotBlank(jsnVariables.getOrDefault("assignee", "").toString()))
            query = taskService.createTaskQuery().processInstanceId(processInstanceId).taskAssignee(jsnVariables.getOrDefault("assignee", "").toString()).orderByTaskName().asc();
        else
            query = taskService.createTaskQuery().processInstanceId(processInstanceId).orderByTaskName().asc();
        List<Task> tasks = null;
        if (query != null) {
            tasks = query.list();
        }
        return tasks;
    }

    /**
     * 查看参与者任务
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public List<Task> queryCandidateUserTask(String candidateUser) throws Exception {
        /** 这里使用TaskService完成任务的查询 */
        List<Task> tasks = taskService.createTaskQuery().taskCandidateUser(candidateUser).list();
        return tasks;
    }
    /**
     * 查看参与者任务
     *
     * @param candidateUser 候选人
     * @version 0.1.0 2016/12/04
     */
    public Page<Task> queryCandidateUserTask(String candidateUser, int pageNum, int pageSize) throws Exception {
        /** 这里使用TaskServ<ice完成任务的查询 */
        TaskQuery query = taskService.createTaskQuery().taskCandidateUser(candidateUser);//这个会自动查出candidateUser所在的候选组和用户组下的任务，也就不用自己去查用户所有的用户组了
        List<Task> tasks = query.listPage((pageNum-1)*pageSize,pageSize);
        long count  = query.count();
        Page page =new Page(tasks);
        page.setTotal(count);
        return page;
    }

    /**
     * 查看组任务
     */

    public List<Task> queryCandidateGroupTask(List<String> candidateGroup) throws Exception {
        /** 这里使用完成任务的查询 */
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroupIn(candidateGroup).list();
        return tasks;
    }

    /**
     * 完成任务
     *
     * @param taskId    任务id
     * @param variables 流程变量
     * @version 0.1.0 2016/12/04
     */

    public void completeTask(String taskId, Map<String, Object> variables) throws Exception {
        /** 这里使用TaskService完成任务的办理 */
        if (variables != null)
            taskService.complete(taskId, variables);
        else
            taskService.complete(taskId);
    }

    /**
     * 实现自动完成任务
     *
     * @param processInstanceId 实例id
     * @param assignee          处理人
     * @param variables         流程变量
     */

    public void completeTaskProcessInstanceId(String processInstanceId, String assignee, Map<String, Object> variables) {
        TaskQuery query = taskService.createTaskQuery().processInstanceId(processInstanceId).taskAssignee(assignee).orderByTaskName().asc();
        List<Task> tasks = query.list();
        for (Task task : tasks) {
            if (variables != null)
                taskService.complete(task.getId(), variables);
            else
                taskService.complete(task.getId());
        }
    }

    /**
     * 获取所有流程实例
     * @return
     */
    public List<ProcessInstance> getProcessInstanceList(String processDefinitionKey){
        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery();
        if (StringUtils.isNotBlank(processDefinitionKey))
            query.processDefinitionKey(processDefinitionKey);
        return query.list();
    }

    public ProcessInstance getProcessInstanceByBusinessKey(String businessKey) throws Exception {
        return runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
    }


    public ProcessInstance getProcessInstanceById(String processInstanceId) throws Exception {
        return runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    }


    public Task getTaskByBusinessKey(String businessKey) {
        // TODO Auto-generated method stub
        return taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
    }

    /**
     * 认领任务 通常一个任务为公共任务任务都有一个以上的候选者
     */
    public void takeTask(String taskId, String userId) throws Exception {
        // claim 认领
        taskService.claim(taskId, userId);
    }

    /**
     * 委托
     */
    public void delegatedTask(String taskId, String userId) throws Exception {
        // 委托
        taskService.delegateTask(taskId, userId);
        // 结果：所有者是Owner李四，处理人Assignee是userId
    }

    /**
     * 流程是否已经结束
     *
     * @param processInstanceId 流程实例ID
     * @version 0.1.0 2016/12/04
     */
    public boolean isFinished(String processInstanceId) {

        return historyService.createHistoricProcessInstanceQuery().finished().processInstanceId(processInstanceId).count() > 0;
    }

    /**
     * 向组任务中添加成员
     */
    public void addGroupUser(String taskId, String userId) {
        taskService.addCandidateUser(taskId, userId);
    }

    /**
     * 向组任务中删除成员
     */
    public void deleteGroupUser(String taskId, String userId) {
        taskService.deleteCandidateUser(taskId, userId);
    }

    /**
     * 查询流程定义
     *
     * @param deploymentId             部署对象id
     * @param processDefinitionId      程定义id
     * @param processDefinitionKey     流程定义key
     * @param processDefinitionKeyLike 流程定义key
     * @param ascOrDesc                降序或升序 1降序 默认升序
     * @version 0.1.0 2016/12/04
     */
    public List<ProcessDefinition> findProcessDefintion(String deploymentId, String processDefinitionId, String processDefinitionKey, String processDefinitionKeyLike, String ascOrDesc) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        // 指定查询条件
        if (StringUtils.isNotBlank(deploymentId)) {
            processDefinitionQuery.deploymentId(deploymentId);// 使用部署对象id查询
        }
        if (StringUtils.isNotBlank(processDefinitionId)) {
            processDefinitionQuery.processDefinitionId(processDefinitionId);// 使用流程定义id查询
        }
        if (StringUtils.isNotBlank(processDefinitionKey)) {
            processDefinitionQuery.processDefinitionKey(processDefinitionKey);// 使用流程定义key查询
        }
        if (StringUtils.isNotBlank(processDefinitionKeyLike)) {
            processDefinitionQuery.processDefinitionKeyLike(processDefinitionKeyLike);// 使用流程定义key模糊查询
        }
        // 排序
        if (StringUtils.isBlank(ascOrDesc) || StringUtils.equalsIgnoreCase(ascOrDesc, "1")) {
            processDefinitionQuery.orderByProcessDefinitionVersion().asc();// 按照版本升序排列
        } else {
            processDefinitionQuery.orderByProcessDefinitionVersion().desc();// 按照版本降序排列
        }
        // 返回结果集
        List<ProcessDefinition> list = processDefinitionQuery.list();// 返回一个集合列表，封装流程定义
        return list;
        /*List<JSONObject> alProcess = new ArrayList<JSONObject>();
        JSONObject jsn;
        if (list != null && list.size() > 0) {
            for (ProcessDefinition pd : list) {
                jsn = (JSONObject) JSONObject.toJSON(pd);
                alProcess.add(jsn);
            }
            list.clear();
        }
        return alProcess;*/
    }

    /**
     * 获得高亮线
     *
     * @param processDefinitionEntity   流程定义实体
     * @param historicActivityInstances 历史活动实体
     * @return 线ID集合
     * @version 0.1.0 2016/12/04
     */
    private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
        List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId
        for (int i = 0; i < historicActivityInstances.size(); i++) {// 对历史流程节点进行遍历
            ActivityImpl activityImpl = processDefinitionEntity.findActivity(historicActivityInstances.get(i).getActivityId());// 得
            // 到节点定义的详细信息
            List<ActivityImpl> sameStartTimeNodes = new ArrayList<ActivityImpl>();// 用以保存后需开始时间相同的节点
            if ((i + 1) >= historicActivityInstances.size()) {
                break;
            }
            ActivityImpl sameActivityImpl1 = processDefinitionEntity.findActivity(historicActivityInstances.get(i + 1).getActivityId());// 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(activityImpl1.getStartTime());
                calendar.set(Calendar.MILLISECOND, 0);
                Calendar calendar1 = Calendar.getInstance();
                calendar1.setTime(activityImpl2.getStartTime());
                calendar1.set(Calendar.MILLISECOND, 0);
                if (calendar.compareTo(calendar1) == 0) {// 如果第一个节点和第二个节点开始时间相同保存
                    ActivityImpl sameActivityImpl2 = processDefinitionEntity.findActivity(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {// 有不相同跳出循环
                    break;
                }
            }
            List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();// 取出节点的所有出去的线
            for (PvmTransition pvmTransition : pvmTransitions) {// 对所有的线进行遍历
                ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition.getDestination();// 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }
        }
        return highFlows;
    }

    /**
     * 流程跟踪图片
     *
     * @param processDefinitionId 流程定义ID
     * @param executionId         流程运行ID
     * @throws Exception
     * @version 0.1.0 2016/12/04
     */
    public InputStream processTracking(String processDefinitionId, String executionId) throws Exception {
        // 当前活动节点、活动线
        List<String> activeActivityIds = new ArrayList<String>();
        List<String> highLightedFlows;
        // 获得当前活动的节点
        if (this.isFinished(executionId)) {// 如果流程已经结束，则得到结束节点
            activeActivityIds.add(historyService.createHistoricActivityInstanceQuery().executionId(executionId).activityType("endEvent").singleResult().getActivityId());
        } else {// 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            activeActivityIds = runtimeService.getActiveActivityIds(executionId);
        }
        // 获得当前活动的节点-结束
        // 获得活动的线
        // 获得历史活动记录实体（通过启动时间正序排序，不然有的线可以绘制不出来）
        List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(executionId).orderByHistoricActivityInstanceStartTime().asc()
                .list();
        // 计算活动线
        highLightedFlows = getHighLightedFlows((ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(processDefinitionId), historicActivityInstances);
        // 获得活动的线-结束
        // 绘制图形
        if (null != activeActivityIds) {
            InputStream imageStream = null;
            try {
                // 根据流程定义ID获得BpmnModel
                BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
                // 输出资源内容到相应对象
                imageStream = new DefaultProcessDiagramGenerator().generateDiagram(bpmnModel, "jpg", activeActivityIds, highLightedFlows, processEngineConfiguration.getActivityFontName(),
                        processEngineConfiguration.getLabelFontName(), processEngineConfiguration.getAnnotationFontName(), processEngineConfiguration.getClassLoader(), 1.0);
                // IOUtils.copy(imageStream, out);
                return imageStream;
            } catch (Exception e) {
                return imageStream;
            } finally {
                IOUtils.closeQuietly(imageStream);
            }
        }
        return null;
    }


    public Collection<FlowElement> queryFlowElement(InputStream resouceStream) {
        Collection<FlowElement> flowElements = null;
        try {
            InputStreamReader in = new InputStreamReader(resouceStream, "UTF-8");
            XMLStreamReader xtr = XMLInputFactory.newInstance().createXMLStreamReader(in);
            BpmnModel model = new BpmnXMLConverter().convertToBpmnModel(xtr);
            flowElements = model.getMainProcess().getFlowElements();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flowElements;
    }

	/*
     * 删除key值相同的所有不同版本的流程定义
	 */

    public void deleteProcessDefinitionByKey(String processDefinitionKey) {
        // 先使用流程定义的key查询流程定义，查询出所有的版本
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey)// 使用流程定义的key查询
                .list();
        // 遍历，获取每个流程定义的部署ID
        if (list != null && list.size() > 0) {
            for (ProcessDefinition pd : list) {
                // 获取部署ID
                String deploymentId = pd.getDeploymentId();
                repositoryService.deleteDeployment(deploymentId, true);
            }
        }
    }


    public InputStream getDiagramByDeploymentId(String deploymentId) throws IOException {
        // 获取图片资源名称
        List<String> list = repositoryService.getDeploymentResourceNames(deploymentId);
        // 定义图片资源的名称
        String resourceName = "";
        if (list != null && list.size() > 0) {
            for (String name : list) {
                if (name.indexOf(".png") >= 0) {
                    resourceName = name;
                }
            }
        }
        return repositoryService.getResourceAsStream(deploymentId, resourceName);
    }


    public Task queryTaskById(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        return task;
    }


    public Map<String, Object> getTaskVariables(String taskId) throws IOException {
        try {
            return taskService.getVariables(taskId);
        } catch (Exception e) {
            return null;
        }
    }

    public HistoricProcessInstance findHistoryProcessInstanceByBusKey(String businessKey) throws Exception {
        return historyService.createHistoricProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey).singleResult();
    }
}

